package cn.pomelo.ws.endpoint;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.pomelo.ws.config.GetHttpSessionConfigurator;
import cn.pomelo.ws.domain.entity.Message;
import cn.pomelo.ws.domain.entity.User;
import cn.pomelo.ws.domain.ex.ServerException;
import cn.pomelo.ws.service.MessageService;
import cn.pomelo.ws.util.Constants;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 测试 websocket 的demo
 */
@Slf4j
@Component
@ServerEndpoint(value = "/test", configurator = GetHttpSessionConfigurator.class)
public class ChatEndpoint implements ApplicationContextAware {

    // 用来向客户端发送消息的对象 一个连接一个 Session 对象
    private Session session;

    @Getter
    private static ApplicationContext applicationContext;

    @Autowired
    public void setApplicationContext(ApplicationContext context) {
        applicationContext = context;
    }

    // 存储在线用户的 session
    private static final Map<Integer, ChatEndpoint> online = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        // 获取登录的用户信息
        User user = (User) config.getUserProperties().get("user");
        log.info("用户 {} 连接成功", user);
        online.put(user.getId(), this);
        try {
            this.session.getBasicRemote().sendText("你已经上线了");
            log.info("发送完毕");
        } catch (IOException e) {
            log.error("发生异常{}", e.getMessage());
            e.printStackTrace();
        }
    }

    @OnMessage
    // 当接受到 客户端 发送的消息时触发 进行触发 可以在这里进行消息转发给指定用户
    public void onMessage(String message, Session session) {
        log.info("来自客户端的消息：{}", message);
        // TODO 将消息转发给指定的用户
        JSONObject entries = JSONUtil.parseObj(message);
        RedisTemplate<String, List<Integer>> redisTemplate = (RedisTemplate<String, List<Integer>>) applicationContext.getBean("listRedisTemplate");
        entries.getJSONArray("receiverId").forEach(receiverId -> {
            List<Integer> onlineUsers = redisTemplate.opsForValue().get(Constants.REDIS_LOGIN);
            if (onlineUsers != null) {
                onlineUsers.forEach(id -> {
                    if (id.equals(receiverId)) {
                        try {
                            Message msg = new Message();
                            msg.setSenderId(entries.getInt("senderId"));
                            msg.setReceiverId((Integer) receiverId);
                            msg.setContent(entries.getStr("content"));
                            msg.setCreateTime(entries.getDate("createTime"));
                            // TODO 将发送的内容存储到数据库
                            MessageService messageService = applicationContext.getBean(MessageService.class);
                            int i = messageService.insertSelective(msg);
                            if (i < 0 ) {
                                throw  new ServerException("存储数据库失败", 201);
                            }
                            log.info("消息存储数据库成功");
                            online.get(id).session.getBasicRemote().sendText(msg.getContent()); // 发送消息
                            log.info("用户{}发送给用户{}完毕,内容：{}", msg.getSenderId(), msg.getReceiverId(), msg.getContent());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        });
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        log.info("用户 {} 退出连接", session.getId());
        log.info("关闭连接原因：{}", closeReason.getReasonPhrase());
        if (session.isOpen()) {
            try {
                session.close();
            } catch (IOException e) {
                log.error("关闭连接异常：{}", e.getMessage());
                e.printStackTrace();
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("发生错误：{}，Session ID： {}", throwable.getMessage(), session.getId());
        throwable.printStackTrace();
    }
}
